package fr.upmc.alasca.centreCalcul.interne.machinePhysique.machineVirtuelle.capteurTempsExecution;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import fr.upmc.alasca.centreCalcul.interne.controleurApplication.ControleurApplication;
import fr.upmc.alasca.centreCalcul.interne.controleurApplication.connectors.ControleurApplicationCapteurTEConnector;
import fr.upmc.alasca.centreCalcul.interne.controleurApplication.ports.ControleurApplicationCapteurTEOutboundPort;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.machineVirtuelle.capteurTempsExecution.interfaces.CapteurTEMachineVI;
import fr.upmc.alasca.centreCalcul.interne.machinePhysique.machineVirtuelle.capteurTempsExecution.ports.CapteurTEMachineVInboundPort;
import fr.upmc.alasca.centreCalcul.objets.Delais;
import fr.upmc.alasca.centreCalcul.objets.Requete;
import fr.upmc.components.AbstractComponent;
import fr.upmc.components.exceptions.ComponentShutdownException;
import fr.upmc.components.ports.PortI;

/**
 * Capteur de calcule du temps d'execution d'une requete.<br/>
 * Toutes les informations relatives a la duree d'execution sont stockees dans
 * l'objet Requete. Cet objet est envoye au controleur d'application
 * correspondant a la requete via le
 * {@linkplain ControleurApplicationCapteurTEOutboundPort}. Le port in
 * correspondant est obtenu a partir de l'Id de l'application stocke dans
 * l'objet Requete avec la chaine :
 * <tt>idApplication + "_ControleurApplication"</tt>. Ce mechanisme permet de
 * recuperer de facon automatique le controleur d'application associe a une
 * requete, sans avoir a gerer des problemes d'abonnements/desabonnements (cf
 * {@linkplain ControleurApplication}).
 * 
 * @author Antoine Lavaire, Sabri Labassi, Thibaut Fleury, Ryme Fantazi
 */
public	class	CapteurTempsExecution
extends			AbstractComponent
{

	private Delais tempsEnvoi;
	private Map<String,List<Requete>> stockageRequete;
	ControleurApplicationCapteurTEOutboundPort cacTEOut;




	public CapteurTempsExecution(String uriMachineVirtuelle, Delais tempsEnvoi) throws Exception {
		super(true,true);

		this.tempsEnvoi = tempsEnvoi;
		
		String uri = uriMachineVirtuelle + "_CapteurTempsExecution";

		// Creation de l'interface du service
		this.addOfferedInterface(CapteurTEMachineVI.class);
		PortI portCapteurTE = new CapteurTEMachineVInboundPort(uri, this);

		this.addPort(portCapteurTE);
		portCapteurTE.localPublishPort();
		
		stockageRequete = new HashMap<String, List<Requete>>();
	}




	/**
	 * Envoi du temps d'execution de la requete au controleur d'application lie
	 * a l'ID application de la requete.
	 * 
	 * @param requete
	 *            La requete.
	 * @throws Exception
	 */
	private void envoyerTEAControleurAdmission() throws Exception {
		//On envoie pas maintenant les requetes, on va les stocker puis les envoyer via le scheduler*/
		synchronized (stockageRequete) {
			cacTEOut.getMesureCapteurTE(stockageRequete);
			stockageRequete.clear();
		}
	}


	public void recupererRequete(final Requete requete, String portUri) {
		synchronized (stockageRequete) {
			if(stockageRequete.get(portUri) != null)
				stockageRequete.get(portUri).add(requete);
			else {
				ArrayList<Requete> listrequetes = new ArrayList<Requete>();
				listrequetes.add(requete);
				stockageRequete.put(portUri, listrequetes);
			}
		}
	}


	public void abonnementCapteurTE(String uriControleurApplication) throws Exception{
		// maintenant on se connecte au controleur d'application
		String idOutPort = uriControleurApplication + "_OutboundPort";
		this.cacTEOut = new ControleurApplicationCapteurTEOutboundPort(
				idOutPort, this);
		
		this.addPort(cacTEOut);

		cacTEOut.localPublishPort();

		String strCacTEIn = uriControleurApplication + "_PushTE";
		cacTEOut.doConnection(strCacTEIn,
				ControleurApplicationCapteurTEConnector.class
						.getCanonicalName());
		
		// on va créer le scheduler pour lancer l'envoi de données vers le controleur d'application toutes les
		// n secondes (définit dans le constructeur)
		ComponentTask componentTask = new ComponentTask() {
			@Override
			public void run() {
				try {
					envoyerTEAControleurAdmission();
				} catch (Exception e) {
					e.printStackTrace();
				}
				
			}
		};
		
		super.scheduleTaskWithFixedDelay(componentTask,0,tempsEnvoi.valeur,tempsEnvoi.unite);
	}


	@Override
	public void shutdown() throws ComponentShutdownException {
		// Appelle de la methode mere
		super.shutdown();
		
		if(cacTEOut != null){
			try {
				cacTEOut.doDisconnection();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}
